package ai4games_tests.soccer;

import game2dai.World;
import game2dai.entities.MovingEntity;
import game2dai.fsm.Dispatcher;
import game2dai.maths.Vector2D;

public class Ball extends MovingEntity implements Constants {

	Pitch pitch;
	PlayerBase owner = null;

	PlayerBase lastkickedBy = null;
	long lastKickedAt = 0;

	double lowGoalY, highGoalY;

	public Ball(Pitch pitch) {
		super("ball", 
				new Vector2D(PITCH_LENGTH/2, PITCH_WIDTH/2),	// Position on pitch
				BALL_RADIUS, 									// Collision radius
				new Vector2D(0,0), 								// Velocity
				Float.MAX_VALUE, 								// Maximum velocity
				new Vector2D(0,1), 								// Heading
				BALL_MASS,										// Ball mass
				1, 												// Max turning rate
				Float.MAX_VALUE									// Maximum force
		);
		this.pitch = pitch;
	}

	public void kick(PlayerBase kicker, Vector2D dir, double force){
		lastkickedBy = kicker;
		lastKickedAt = System.currentTimeMillis();	
		dir.normalize();
		dir.mult(force/mass);
		velocity.set(dir);
		owner = null;
	}

	public boolean readyToBeKickedBy(PlayerBase kicker){
		if(lastkickedBy == kicker && System.currentTimeMillis() - lastKickedAt  < PlayerKickInterval)
			return false;	
		return true;
	}

	public PlayerBase getLastKickedBy(){
		return lastkickedBy;
	}

	public double timeToCoverDistance(Vector2D from, Vector2D to, double force){
		//this will be the velocity of the ball in the next time step *if*
		//the player was to make the pass. 
		double speed = force / mass;

		//calculate the velocity at B using the equation
		//
		//  v^2 = u^2 + 2as
		//

		//first calculate s (the distance between the two positions)
		double DistanceToCover =  Vector2D.dist(from, to);

		double term = speed * speed + 2.0 * DistanceToCover * FRICTION_MAG;

		//if  (u^2 + 2as) is negative it means the ball cannot reach point B.
		if (term <= 0.0) return -1.0;

		double v = Math.sqrt(term);

		//it IS possible for the ball to reach B and we know its speed when it
		//gets there, so now it's easy to calculate the time using the equation
		//
		//    t = v-u
		//        ---
		//         a
		//
		return (v-speed)/FRICTION_MAG;		
	}

	public Vector2D futurePosition(double time){
		//using the equation s = ut + 1/2at^2, where s = distance, a = friction
		//u=start velocity

		// start by calculating the ut term, which is a vector
		Vector2D s = Vector2D.mult(velocity, time);

		//calculate the 1/2at^2 term, which is scalar
		double half_a_t_squared = 0.5 * FRICTION_MAG * time * time;

		//turn the scalar quantity into a vector by multiplying the value with
		//the normalized velocity vector (because that gives the direction)
		Vector2D dir = Vector2D.normalize(velocity);
		dir.mult(half_a_t_squared);

		// Calculate the estimated position
		s.add(dir);
		//the predicted position is the balls position plus these two terms
		return s;
	}

	public void trap(PlayerBase owner){
		this.owner = owner;
		velocity.set(Vector2D.ZERO);
	}

	public void realease(){
		owner = null;
	}

	public boolean keeperHasBall(){
		return owner != null && owner.isGoalKeeper();
	}

	public void toCentreSpot(double x, double y){
		pos.set(x,y);
		velocity.set(0,0);
		heading.set(0,1);
	}

	public void update(double deltaTime, World world) {
		super.update(deltaTime, world);
		if(velocity.lengthSq() > deltaTime * FRICTION_MAG_SQ){
			Vector2D decel = Vector2D.normalize(velocity);
			decel.mult(deltaTime * FRICTION_MAG);
			velocity.add(decel);
		}
		else
			velocity.set(0,0);
		// Now check edge conditions
		boolean goalScored = false;
		if(pos.x - BALL_RADIUS < 0){
			velocity.x = Math.abs(velocity.x);
			goalScored = pos.y >= GOAL_LOW_Y && pos.y <= GOAL_HIGH_Y;
		}
		else if(pos.x + BALL_RADIUS > PITCH_LENGTH){
			velocity.x = -Math.abs(velocity.x);
			goalScored = pos.y >= GOAL_LOW_Y && pos.y <= GOAL_HIGH_Y;			
		}
		if(pos.y - BALL_RADIUS < 0)
			velocity.y = Math.abs(velocity.y);
		else if(pos.y + BALL_RADIUS > PITCH_WIDTH)
			velocity.y = -Math.abs(velocity.y);
		if(goalScored){
			velocity.set(0,0);
			int teamNo = pos.x < PITCH_LENGTH/2 ? 1 : 0;
			Dispatcher.dispatch(0, entityID, pitch.ID(), GOAL_SCORED, teamNo);
		}
	}


}
